package com.yyh.synctool.client.tool;

import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * 整合mybatis
 * 动态加载数据库连接配置
 * 动态加载xml映射配置
 * @author xuan
 * @date 2021/12/14
 */
public class DBHelper
{
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 初始化DBHelper
     * 能够在程序运行时动态加载数据库连接配置
     */
    public void init(String driverName, String url, String username, String password) {
        DataSource dataSource = getUnpooledDataSource(driverName, url, username, password);
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("prod", transactionFactory, dataSource);
        Configuration configuration = new Configuration(environment);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

        this.sqlSessionFactory = sqlSessionFactory;
    }

    /**
     * 注册Mapper
     * 动态加载mybatis的sql语句对应的xml映射文件
     */
    public void registryMapper(String namespace, String xml) {
        Configuration configuration = this.sqlSessionFactory.getConfiguration();

        ByteArrayInputStream xmlBytes = new ByteArrayInputStream(xml.getBytes());
        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(
                xmlBytes, configuration, namespace, configuration.getSqlFragments());
        xmlMapperBuilder.parse();
    }

    /** 返回Mapper的接口对象，通过动态代理关闭session连接 */
    public <T> T getMapper(Class<T> aClass) {
        SqlSession session = this.sqlSessionFactory.openSession();
        T mapper = session.getMapper(aClass);

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                // 执行方法
                Object ret = method.invoke(mapper, args);

                //关闭连接
                session.close();

                return ret;
            }
        };

        T proxyMapper = (T) Proxy.newProxyInstance(mapper.getClass().getClassLoader(),
                mapper.getClass().getInterfaces(), handler);

        return proxyMapper;
    }

    /** 数据库连接测试 */
    public boolean isAvailable() {
        try {
            DataSource dataSource = sqlSessionFactory.getConfiguration().getEnvironment().getDataSource();
            dataSource.getConnection().close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /** sql语句测试 */
    public boolean sqlAvailable(String sqlId) {
        try {
            SqlSession session = sqlSessionFactory.openSession();
            List<Object> list = session.selectList(sqlId);
            session.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /** 执行sql语句 */
    public List selectList(String sqlId) {
        SqlSession session = sqlSessionFactory.openSession();
        List<Object> list = session.selectList(sqlId);
        session.close();
        return list;
    }

    /** 获取数据源 --不带有连接池 */
    private DataSource getUnpooledDataSource(String driverName, String url, String username, String password) {
        UnpooledDataSource unpooledDataSource = new UnpooledDataSource();
        unpooledDataSource.setDriver(driverName);
        unpooledDataSource.setUrl(url);
        unpooledDataSource.setUsername(username);
        unpooledDataSource.setPassword(password);
        unpooledDataSource.setAutoCommit(false);

        return unpooledDataSource;
    }
}
